﻿namespace EFS.RC.CC.Sessions.OfficeService.Operators.HuntQueue
{
	/// <summary>
	/// Operator dial in session
	/// </summary>
	internal class DialIn : EFS.RC.CC.Sessions.OfficeService.Operators.Operator
	{
		#region Internals

		// Dial conversation
		private Microsoft.Rtc.Collaboration.Conversation dialConversation;

		// Dial to Agent call
		private Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCall dialAudioVideoCall = null;

		/// <summary>
		/// Dial status
		/// </summary>
		public enum DialHuntStatus
		{
			Idle,
			Dial,
			Answered,
			NotAnswered,
			Declined,
			Error
		}

		// dial status
		private EFS.RC.CC.Sessions.OfficeService.Operators.HuntQueue.DialIn.DialHuntStatus dialStatus = EFS.RC.CC.Sessions.OfficeService.Operators.HuntQueue.DialIn.DialHuntStatus.Idle; 

		#endregion

		#region Constructor

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="conversation"></param>
		/// <param name="remoteEndpoint"></param>
		public DialIn(EFS.RC.CC.Presences.Operators.Presence operatorPresence, EFS.RC.CC.Sessions.OfficeService.Customers.Customer customerSession)
			: base(operatorPresence, customerSession)
		{
			// check
			EFS.Common.Verify.Reference(operatorPresence, "operatorPresence");

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(CustomerSession.ConversationGuid, "DialIn:ctor", OperatorPresence.User.SIP, GetType()); 
		}

		#endregion

		#region Usage

		/// <summary>
		/// Cancel dial
		/// </summary>
		public void BeginCancelDial()
		{
			// sync access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check still dialing
				if (EFS.RC.CC.Sessions.OfficeService.Operators.HuntQueue.DialIn.DialHuntStatus.Dial == dialStatus)
				{
					// terminate dialing 
					EFS.RC.CC.Tools.Functions.Termination termination = new Tools.Functions.Termination(this);
					termination.Terminate(dialAudioVideoCall);
					termination.Terminate(dialConversation);
				}
			}
		}

		/// <summary>
		/// Describe this object into a nice formated string
		/// </summary>
		/// <returns></returns>
		public override System.String Describe()
		{
			return System.String.Format(System.String.Format("EFS.RC.CC.Sessions.OfficeService.Operators.HuntQueue.DialIn [{0}] [{1}]", OperatorPresence.Describe(), CustomerSession.Describe()));
		}

		/// <summary>
		/// Dial status
		/// </summary>
		public EFS.RC.CC.Sessions.OfficeService.Operators.HuntQueue.DialIn.DialHuntStatus DialStauts
		{
			get { return dialStatus; }
		}

		/// <summary>
		/// Remote conversation id
		/// </summary>
		public override System.String RemoteConversationId
		{
			get 
			{
				EFS.Common.Verify.Reference(dialConversation, "dialConversation"); 
				return dialConversation.Id; 
			}
		}

		#endregion

		#region Startup

		/// <summary>
		/// Startup
		/// </summary>
		protected override void SetupSession()
		{
			// log
			EFS.EventLog.Log.LogInfo("SetupSession", this);

			// register opertor session
			CustomerSession.OfficeServiceConference.SetOperatorSession(this);

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(CustomerSession.ConversationGuid, "SetupSession", this);

			// sync access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check idle
				if (EFS.RC.CC.Sessions.OfficeService.Operators.HuntQueue.DialIn.DialHuntStatus.Idle == dialStatus)
				{
					// not answered
					dialStatus = EFS.RC.CC.Sessions.OfficeService.Operators.HuntQueue.DialIn.DialHuntStatus.Dial;
				}
			}

			// make timo out
			EFS.RC.CC.Tools.Schedulers.AgentDialTimeOut agentDialTimeOut = new EFS.RC.CC.Tools.Schedulers.AgentDialTimeOut(this);
			agentDialTimeOut.Start();

			// get end point
			Microsoft.Rtc.Collaboration.ApplicationEndpoint applicationEndpoint = CustomerSession.CustomersOfficeServiceEndPointApplication.CollaborationApplicationEndpoint;

			// impersonated uri
			System.String impersonatedUri = System.String.Format("sip:service-center@{0}", applicationEndpoint.DefaultDomain);

			// dial to agent
			dialConversation = new Microsoft.Rtc.Collaboration.Conversation(applicationEndpoint);
			dialConversation.Impersonate(impersonatedUri, null, CustomerSession.DisplayName);
			dialConversation.ApplicationContext = this;

			// make call
			dialAudioVideoCall = new Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCall(dialConversation);

			// options
			Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCallEstablishOptions dialAudioVideoCallOptions = new Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCallEstablishOptions();

			// establish call
			dialAudioVideoCall.BeginEstablish(
				OperatorPresence.User.SIP,
 				dialAudioVideoCallOptions,
				EFS.Common.Async.AsyncResult.StateDelegate.End,
				EFS.Common.Async.AsyncResult.StateDelegate.State(OnEndEstablishDialCall));
		}

		/// <summary>
		/// End dial call
		/// </summary>
		/// <param name="ar"></param>
		private void OnEndEstablishDialCall(System.IAsyncResult ar)
		{
			EFS.EventLog.Log.LogInfo("OnEndEstablishDialCall", this);

			try
			{
				// end establish call
				dialAudioVideoCall.EndEstablish(ar);

				// transfer to conference
				OfficeServiceConference.BeginTransfer(dialAudioVideoCall, OnTransferOperatorFailed);
			}
			catch (System.Exception ex)
			{
				try
				{
					// handle ex
					OnDialOperatorFailed(ex);
				}
				catch (System.Exception ex1)
				{
					// handle exception
					HandleExceptionSafe(ex1, OnException);
				}
				
				// handle exception
				HandleExceptionSafe(ex, OnException);
			}
		}

		/// <summary>
		/// Dial out failed
		/// </summary>
		private void OnDialOperatorFailed(System.Exception ex)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnDialOutFailed", this);

			// sync access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check still dialing
				if (EFS.RC.CC.Sessions.OfficeService.Operators.HuntQueue.DialIn.DialHuntStatus.Dial == dialStatus)
				{
					// check reason
					if (ex.Message.IndexOf("userNotAnswered") > 0)
					{
						dialStatus = EFS.RC.CC.Sessions.OfficeService.Operators.HuntQueue.DialIn.DialHuntStatus.NotAnswered;
					}
					else if (ex.Message.IndexOf("userDeclined") > 0)
					{
						dialStatus = EFS.RC.CC.Sessions.OfficeService.Operators.HuntQueue.DialIn.DialHuntStatus.Declined;
					}
					else
					{
						dialStatus = EFS.RC.CC.Sessions.OfficeService.Operators.HuntQueue.DialIn.DialHuntStatus.Error;
					}
				}
			}

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(CustomerSession.ConversationGuid, "OnDialOperatorFailed", this); 

			// shut down
			BeginShutdown();
		}

		/// <summary>
		/// Transfer failed
		/// </summary>
		private void OnTransferOperatorFailed(System.Exception ex)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnTransferOperatorFailed", this);

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(CustomerSession.ConversationGuid, "OnTrasferOperatorFailed", this);

			// shut down
			BeginShutdown();
		}

		#endregion

		#region Runtime

		/// <summary>
		/// Called when participant end point added
		/// </summary>
		protected override void ParticipantEndPointAdded()
		{
			// call base
			base.ParticipantEndPointAdded();

			// sync access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check was dialing
				if (EFS.RC.CC.Sessions.OfficeService.Operators.HuntQueue.DialIn.DialHuntStatus.Dial == dialStatus)
				{
					// all ok
					dialStatus = EFS.RC.CC.Sessions.OfficeService.Operators.HuntQueue.DialIn.DialHuntStatus.Answered;
				}
			}

			// established
			AsinkEstablishedDelegate.Start();

			// stop all music
			CustomerSession.OfficeServiceConference.StopAllPlayers();
		}

		#endregion

		#region Shutdown

		/// <summary>
		/// Cleanup
		/// </summary>
		protected override void CleanupSession()
		{
			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(CustomerSession.ConversationGuid, "CleanupSession", this);
		}

		#endregion
	}
}
